#include "ServiceEntrance.hpp"


ServiceEntrance::ServiceEntrance(HttpServer* server, ThreadPool* pool) {
    server->setRoute("/", std::bind(&ServiceEntrance::main, this, std::placeholders::_1));
    server->setRoute("/createChatCinema", std::bind(&ServiceEntrance::createChatCinema, this, std::placeholders::_1));
    server->setRoute("/joinChatCinema", std::bind(&ServiceEntrance::joinChatCinema, this, std::placeholders::_1));
    server->setRoute("/cinema", std::bind(&ServiceEntrance::cinema, this, std::placeholders::_1));


    server->setRoute("/favicon.ico", std::bind(&ServiceEntrance::favicon, this, std::placeholders::_1));
    server->setRoute("/bird.ico", std::bind(&ServiceEntrance::bird, this, std::placeholders::_1));

    server->setRoute("/video", std::bind(&ServiceEntrance::video, this, std::placeholders::_1));
    server->setRoute("/upgrade", std::bind(&ServiceEntrance::upgrade, this, std::placeholders::_1));
    threadpool_ = pool;

    server->setMessageCall(std::bind(&ServiceEntrance::onWebSocketMessage, this, std::placeholders::_1));

}


void ServiceEntrance::favicon(ConnectionPtr conn) {
    auto content = file_get_content("favicon.ico");
    HttpResponse resp;
    resp.addContent(content.c_str(), content.size());
    resp.writeBuffer(conn->getOutputBuffer());
    conn->writeSocket();
}

void ServiceEntrance::bird(ConnectionPtr conn) {
    auto content = file_get_content("bird.ico");
    HttpResponse resp;
    resp.addContent(content.c_str(), content.size());
    resp.writeBuffer(conn->getOutputBuffer());
    conn->writeSocket();
}




void ServiceEntrance::video(ConnectionPtr conn) {

    auto range = conn->getHttpRequest() -> getHeader("Range");
    int start = 6;
    int end = range.find("-");
    long filestart = atol(range.substr(start, end-start).c_str());
    const long buffersize = 8*1024*1024; // 每次发送的大小


    // 读取对应的文件部分
    long fileend = buffersize + filestart-1;
    if (end+1 != range.size()) {
        fileend = atol(range.substr(end+1, range.size() - end).c_str());
    }

    auto [content, filelen] = file_get_content_bytes("public_resources/output.mkv", filestart, fileend);


    // 构建HttpResponse
    fileend = std::min(filelen-1, fileend);
    HttpResponse resp;
    resp.setContentType("video/mp4");
    resp.setStatus(HttpStatusCode::PartialContent);
    resp.addContent(content.c_str(), content.size());
    resp.addHeader("Accept-Ranges", "bytes");
    resp.addHeader("Content-Range", "bytes " + std::to_string(filestart) + "-" + std::to_string(fileend) + "/" + std::to_string(filelen));

    // 将response写入buffer
    resp.writeBuffer(conn->getOutputBuffer()); 
    
    // 较大文件的发送，用线程或线程池防止发送阻塞 TODO: 用协程来进行发送，减少切换开销
    // threadpool_ -> submitTask([conn](){
    //     conn->writeSocket();    
    // });
    std::thread t([conn](){
        conn->writeSocket(); 
    });
    t.detach();
}




void ServiceEntrance::upgrade(ConnectionPtr conn) {
    // 用于升级websocket
    HttpResponse resp;
    auto request = conn->getHttpRequest();
    if (request->getHeader("Connection")!= "Upgrade" || request->getHeader("Upgrade")!= "websocket") {
        resp.writeBuffer(conn->getOutputBuffer());
        conn->writeSocket();
        return ;
    }

    auto websocketVersion = request->getHeader("Sec-WebSocket-Version");
    auto websocketKey = request->getHeader("Sec-WebSocket-Key");
    if (websocketKey.empty()) {
        resp.writeBuffer(conn->getOutputBuffer());
        conn->writeSocket();
        return ;
    }

    resp.setWebSocketResponse(websocketKey);

    resp.writeBuffer(conn->getOutputBuffer());
    // conn->getOutputBuffer() ->printBuff();
    conn->writeSocket();
    conn->upgradToWS();

}


void ServiceEntrance::main(ConnectionPtr conn){
    auto content = file_get_content("main.html");
    HttpResponse resp;
    resp.addContent(content.c_str(), content.size());
    resp.addHeader("Content-Type", "text/html; charset=utf-8");
    resp.writeBuffer(conn->getOutputBuffer());
    conn->writeSocket();
}

void ServiceEntrance::createChatCinema(ConnectionPtr conn){
    auto request = conn->getHttpRequest();
    json js = json::parse(request->getBody());
    auto hostName = js["hostName"].get<std::string>();
    auto roomName = js["roomName"].get<std::string>();

    // std::cout << hostName << roomName << std::endl;

    if (chatRooms_.find(roomName) != chatRooms_.end() && chatRooms_[roomName]->getUserNum() != 0) {
        HttpResponse response;
        std::string message = "{\"message\": \"该放映室名称已存在！\", \"status\":\"0\"}";
        response.addContent(message.c_str(), message.size());
        response.writeBuffer(conn->getOutputBuffer());
        conn->writeSocket();
        return;
    }
    chatRooms_[roomName] = std::make_shared<ChatCinema>(roomName, hostName);
    HttpResponse response;
    std::string message = "{\"message\": \"创建成功\", \"status\":\"1\"}";
    response.addContent(message.c_str(), message.size());
    response.writeBuffer(conn->getOutputBuffer());
    conn->writeSocket();
}


void ServiceEntrance::joinChatCinema(ConnectionPtr conn){
    auto request = conn->getHttpRequest();
    json js = json::parse(request->getBody());
    auto userName = js["userName"].get<std::string>();
    auto roomName = js["roomName"].get<std::string>();
    if (chatRooms_.find(roomName) == chatRooms_.end()) {
        HttpResponse response;
        std::string message = "{\"message\": \"该放映室不存在！\", \"status\":\"0\"}";
        response.addContent(message.c_str(), message.size());
        response.writeBuffer(conn->getOutputBuffer());
        conn->writeSocket();
        return;
    }

    if (chatRooms_[roomName]->haveUser(userName)) {
        HttpResponse response;
        std::string message = "{\"message\": \"该放映室有人跟你的昵称一样！请修改下！\", \"status\":\"0\"}";
        response.addContent(message.c_str(), message.size());
        response.writeBuffer(conn->getOutputBuffer());
        conn->writeSocket();
        return ;
    }
    chatRooms_[roomName] -> addUser(userName);
    HttpResponse response;
    std::string message = "{\"message\": \"加入成功！\", \"status\":\"1\"}";
    response.addContent(message.c_str(), message.size());
    response.writeBuffer(conn->getOutputBuffer());
    conn->writeSocket();
}



void ServiceEntrance::cinema(ConnectionPtr conn) {
    auto content = file_get_content("cinema.html");
    HttpResponse resp;
    resp.addContent(content.c_str(), content.size());
    resp.addHeader("Content-Type", "text/html; charset=utf-8");
    resp.writeBuffer(conn->getOutputBuffer());
    conn->writeSocket();
}



void ServiceEntrance::onWebSocketMessage(ConnectionPtr conn) {
    if (!conn->isWebSocket()) {
        return ;
    }
    auto wsd = conn->getWSData();

    std::cout << wsd->getPayLoad() << std::endl;
    json js = json::parse(wsd->getPayLoad());
    auto action = js["action"].get<std::string>();
    if (action == "addUserWebConn") {
        if (!js.contains("userName") || !js.contains("roomName")) {
            return;
        }
        auto userName = js["userName"].get<std::string>();
        auto roomName = js["roomName"].get<std::string>();
        
        bool res = addUserWebConn(userName, roomName, conn);
        if (res) {
            broadcast("[SYSTEM]", roomName, userName+" 加入了放映室");
            addHistory("[SYSTEM]", userName+" 加入了放映室", roomName);
        }
        
        return;
    }
    if (action == "chat") {
        if (!js.contains("userName") || !js.contains("roomName") || !js.contains("message")) {
            return;
        }
        auto userName = js["userName"].get<std::string>();
        auto roomName = js["roomName"].get<std::string>();
        auto message = js["message"].get<std::string>();
        broadcast(userName, roomName, message);
        addHistory(userName, message, roomName);
        return;
    }

    if (action == "getHistory") {
        if (!js.contains("roomName")) {
            return;
        }
        auto roomName = js["roomName"].get<std::string>();
        auto history = chatRooms_[roomName]->getHistory();
        json historyJs;
        historyJs["action"] = action;
        historyJs["history"] = {};
        int length = 0;
        for (auto it=history.rbegin(); it != history.rend(); ++it) {
            if (length >= 10000) break;
            historyJs["history"].push_back({it->first, it->second});
            length += it->first.size() + it->second.size();
        }
        WebSocketData wsd;
        std::cout << historyJs.dump() << std::endl;
        wsd.setPayLoad(historyJs.dump());
        
        wsd.writeToBuffer(conn->getOutputBuffer());
        conn->writeSocket();
    }

    // 以下为测试代码

    wsd->setPayLoad("{\"userName\":\"test\",\"message\":\"hello\"}");
    wsd->writeToBuffer(conn->getOutputBuffer());
    conn->writeSocket();

}

void ServiceEntrance::addHistory(const std::string& username, const std::string& message, const std::string& roomname){
    auto room = chatRooms_[roomname];
    room->addHistory(username, message);
}


bool ServiceEntrance::addUserWebConn(const std::string& username, const std::string& roomName, ConnectionPtr conn){
    if (chatRooms_.find(roomName)==chatRooms_.end()) {
        return false;
    }
    auto chatRoom = chatRooms_[roomName];
    chatRoom->addUser(username, conn);
    return true;
}

void ServiceEntrance::broadcast(const std::string& username, const std::string& roomName, const std::string& message){
    auto room = chatRooms_[roomName];
    auto connections = room->getConnections();
    WebSocketData wsd;
    wsd.setPayLoad("{\"userName\":\""+username+"\",\"message\":\""+message+"\", \"action\":\"chat\"}");
    for (auto it=connections.begin(); it != connections.end(); ++it) {
        if (it->second.expired()) {
            room->removeUser(it->first);
            continue;
        }
        auto connptr = it->second.lock();
        wsd.writeToBuffer(connptr->getOutputBuffer());
        connptr->writeSocket();
    }
}


void ServiceEntrance::onWebSocketClose(ConnectionPtr conn){
    
}